package com.pzcnet.config;

import com.pzcnet.annotation.CallBackHandler;
import com.pzcnet.annotation.EventHandler;
import com.pzcnet.annotation.EventProcessHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by LK on 2017/9/12.
 */
@Component
public class EventConfig implements BeanPostProcessor {

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Describe {
        private String beanName;
        private Object bean;
        private Method method;
        private String[] parameterNames;
    }

    private static final Map<String, Describe> eventProcessMap = new HashMap<>();
    private static final Map<String, Describe> eventCallBackMap = new HashMap<>();
    private static final Map<String, String[]> methodParameterNames = new HashMap<>();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        EventHandler eventHandler = AnnotationUtils.findAnnotation(bean.getClass(), EventHandler.class);
        ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

        if (eventHandler != null) {
            Method[] methods = bean.getClass().getMethods();

            for (Method method : methods) {
                EventProcessHandler eventProcessHandler = AnnotationUtils.findAnnotation(method, EventProcessHandler.class);
                if (eventProcessHandler != null) {
                    String[] parameterNames = parameterNameDiscoverer
                            .getParameterNames(method);
                    methodParameterNames.put(beanName + method.getName(), parameterNames);
                }
            }
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        this.scanEventHandler(bean, beanName);
        this.scanCallBackHandler(bean, beanName);
        return bean;
    }

    private void scanEventHandler(Object bean, String beanName) {
        EventHandler eventHandler = AnnotationUtils.findAnnotation(bean.getClass(), EventHandler.class);

        if (eventHandler != null) {
            Method[] methods = bean.getClass().getMethods();

            for (Method method : methods) {
                EventProcessHandler eventProcessHandler = AnnotationUtils.findAnnotation(method, EventProcessHandler.class);
                if (eventProcessHandler != null) {
                    String[] parameterNames = methodParameterNames.get(beanName + method.getName());

                    eventProcessMap.put(eventProcessHandler.value().toString(),
                            Describe.builder()
                                    .bean(bean)
                                    .beanName(beanName)
                                    .method(method)
                                    .parameterNames(parameterNames)
                                    .build());
                }
            }
        }
    }

    private void scanCallBackHandler(Object bean, String beanName) {
        CallBackHandler callBackHandler = AnnotationUtils.findAnnotation(bean.getClass(), CallBackHandler.class);

        if (callBackHandler != null) {
            eventCallBackMap.put(callBackHandler.value().toString(),
                    Describe.builder()
                            .bean(bean)
                            .beanName(beanName)
                            .build()
            );
        }
    }

    public static Map<String, Describe> getEventProcessMap() {
        return eventProcessMap;
    }

    public static Map<String, Describe> getEventCallBackMap() {
        return eventCallBackMap;
    }
}
